package com.yanmaohu.nikita.parser;

import com.yanmaohu.nikita.NikitaException;
import com.yanmaohu.nikita.data.expression.Expression;
import com.yanmaohu.nikita.data.expression.NameExpression;
import com.yanmaohu.nikita.data.function.FunctionBody;
import com.yanmaohu.nikita.data.function.ParameterList;
import com.yanmaohu.nikita.data.structure.*;
import com.yanmaohu.nikita.scanner.SourceCode;
import com.yanmaohu.nikita.scanner.Token;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Bright Lee
 */
public final class MemberParser {

    private Parser parser;
    private ExpressionParser expressionParser;
    private ModifierParser modifierParser;
    private StatementParser statementParser;

    protected MemberParser(Parser parser) {
        this.parser = parser;
    }

    public void setExpressionParser(ExpressionParser expressionParser) {
        this.expressionParser = expressionParser;
    }

    public void setModifierParser(ModifierParser modifierParser) {
        this.modifierParser = modifierParser;
    }

    public void setStatementParser(StatementParser statementParser) {
        this.statementParser = statementParser;
    }

    private File getFile() {
        return parser.getFile();
    }

    private SourceCode getCode() {
        return parser.getCode();
    }

    protected StructureBody parseStructureMemberList() {
        final Token token = scan();
        if (!token.isLeftBrc()) {
            throw new NikitaException(getCode(), token, "This must be a {.");
        }
        final int beginIndex = token.getBeginIndex();
        List<Member> memberList = null;
        while (true) {
            Member member = tryParseStructureMember();
            if (member == null) {
                break;
            }
            if (memberList == null) {
                memberList = new ArrayList<>();
            }
            memberList.add(member);
        }
        final Token token1 = scan();
        if (!token1.isRightBrc()) {
            throw new NikitaException(getCode(), token1, "This must be }.");
        }
        final int endIndex = token1.getEndIndex();
        return new StructureBody(beginIndex, endIndex, memberList);
    }

    protected Member tryParseStructureMember() {
        final int markedIndex = mark();
        MemberModifier memberModifier = modifierParser.tryParseMemberModifier();
        Token token = scan();
        if (!token.isId() && memberModifier == null) {
            reset(markedIndex);
            return null;
        }
        NameExpression nameExpression = new NameExpression(token);
        int markedIndex1 = mark();
        Token token1 = scan();
        if (token1.isSemicolon()) {
            return new VariableMember(memberModifier, nameExpression, null);
        }
        if (token1.isAssign()) {
            Expression variableValue = expressionParser.parseExpression();
            token1 = scan();
            if (!token1.isSemicolon()) {
                throw new NikitaException(getCode(), token1, "Missing a ;.");
            }
            return new VariableMember(memberModifier, nameExpression, variableValue);
        }
        if (token1.isLeftBrc()) {
            reset(markedIndex1);
            StructureBody structureBody = parseStructureMemberList();
            return new StructureMember(memberModifier, nameExpression, structureBody);
        }
        if (token1.isLeftPar()) {
            reset(markedIndex1);
            ParameterList parameterList = parser.parseParameterList();
            FunctionBody functionBody = statementParser.parseStatementList();
            int markedIndex2 = mark();
            Token token2 = scan();
            reset(markedIndex2);
            if (token2.isSemicolon()) {
                throw new NikitaException(getCode(), token2, "Delete the semicolon.");
            }
            return new FunctionMember(memberModifier, nameExpression, parameterList, functionBody);
        }
        throw new NikitaException(getCode(), token1, "Error token.");
    }

    int mark() {
        return parser.mark();
    }

    Token scan() {
        return parser.scan();
    }

    void reset(int markedIndex) {
        parser.reset(markedIndex);
    }

    void reset() {
        parser.reset();
    }

}
